/**
 * Copyright (c) 2005-20010 springside.org.cn
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * 
 * $Id: PropertyFilter.java,v 1.1 2013/07/23 06:17:04 qiujiwei Exp $
 */
package utils.orm.jpa;

import java.io.Serializable;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import org.apache.commons.lang.StringUtils;

/**
 * 与具体ORM实现无关的属性过滤条件封装类, 主要记录页面中简单的搜索过滤条件.
 * <p>示例: LIKES_t!NAME_OR_b!LOGINNAME
 * <p>
 * 支持数据类型:S(String.class), I(Integer.class), L(Long.class), N(Double.class), D(Date.class), B(Boolean.class),
 *				O(Object.class),C(Character.class),G(BigDecimal.class), Y(Byte.class),R(Short.class);  
 *<p>
 * 支持比较类型： EQ:"=";
 * 				NOEQ:"<>";
 * 				LIKE:" like ";
 * 				LIKEL:" like ";
 * 				LIKER: " like ";
 * 				LT: "<";
 * 				GT: ">";
 * 				LE: "<=";
 * 				GE: ">=";
 * 				IN: " in ";

 * @author calvin
 */
public class PropertyFilter implements Serializable {

	/** 多个属性间OR关系的分隔符. */
	public static final String OR_SEPARATOR = "_OR_";

	/** 属性比较类型. */
	public enum MatchType {
		EQ, NOEQ, LIKE, LIKEL, LIKER, LT, GT, LE, GE, IN, LET;
		@Override
		public String toString() {
			switch (this) {
			case EQ:
				return "=";
			case NOEQ:
				return "<>";
			case LIKE:
				return " like ";
			case LIKEL:
				return " like ";
			case LIKER:
				return " like ";
			case LT:
				return "<";
			case GT:
				return ">";
			case LET:
				return "<=";
			case LE:
				return "<=";
			case GE:
				return ">=";
			case IN:
				return " in ";
			default:
				throw new IllegalArgumentException("枚举类型未知");
			}
		}
	}

	/** 属性数据类型. */
	public enum PropertyType {
		S(String.class), I(Integer.class), L(Long.class), N(Double.class), D(Date.class), B(Boolean.class), O(Object.class), C(Character.class), G(BigDecimal.class), Y(Byte.class), R(Short.class); // ,BigInteger,
																																																		// Double,
																																																		// Float

		private Class<?> clazz;

		private PropertyType(Class<?> clazz) {
			this.clazz = clazz;
		}

		public Class<?> getValue() {
			return clazz;
		}
	}

	private MatchType matchType = null;
	private Object matchValue = null;

	private Class<?> propertyClass = null;
	// 含前缀的属性名，eg:{"o.name","n.name")
	private String[] propertyFullNames = null;
	// 不含前缀的属性名，eg:{"name","code"}
	private String[] propertyNames = null;
	// 同一次查询时同名参数序号
	private Integer[] ordernos;

	public PropertyFilter() {
	}

	/**
	 * @param filterName
	 *            比较属性字符串,含待比较的比较类型、属性值类型及属性列表. eg. LIKES_NAME_OR_LOGIN_NAME
	 * @param value
	 *            待比较的值.
	 * @throws Exception 
	 */
	@SuppressWarnings("static-access")
	public PropertyFilter(final String filterName, final Object value) throws Exception {

		String firstPart = StringUtils.substringBefore(filterName, "_");
		String matchTypeCode = StringUtils.substring(firstPart, 0, firstPart.length() - 1);
		String propertyTypeCode = StringUtils.substring(firstPart, firstPart.length() - 1, firstPart.length());

		try {
			matchType = Enum.valueOf(MatchType.class, matchTypeCode);
		} catch (RuntimeException e) {
			throw new IllegalArgumentException("filter名称" + filterName + "没有按规则编写,无法得到属性比较类型.", e);
		}

		try {
			propertyClass = Enum.valueOf(PropertyType.class, propertyTypeCode).getValue();
		} catch (RuntimeException e) {
			throw new IllegalArgumentException("filter名称" + filterName + "没有按规则编写,无法得到属性值类型.", e);
		}

		String propertyFullNameStr = StringUtils.substringAfter(filterName, "_");
		if(StringUtils.isBlank(propertyFullNameStr)){
			throw new IllegalArgumentException("filter名称" + filterName + "没有按规则编写,无法得到属性名称.");
		}
		propertyFullNames = StringUtils.splitByWholeSeparator(propertyFullNameStr, PropertyFilter.OR_SEPARATOR);
		propertyNames = new String[propertyFullNames.length];
		setOrdernos(new Integer[propertyFullNames.length]);
		for (int i = 0; i < propertyFullNames.length; i++) {
			getOrdernos()[i] = 0;
			propertyNames[i] = StringUtils.substringAfter(propertyFullNames[i], "!");
		}
		if (value instanceof String) {
			this.matchValue = convertStringToObject((String) value, propertyClass);
		} else {
			if (!propertyClass.isAssignableFrom(Object.class) && !propertyClass.isAssignableFrom(value.getClass())) {
				throw new IllegalArgumentException("filter类型与值的实际类型不匹配", null);
			}
			this.matchValue = value;
		}
		if (matchType == MatchType.LIKE && propertyClass == String.class) {
			matchValue = "%" + matchValue + "%";
		} else if (matchType == MatchType.LIKEL && propertyClass == String.class) {
			matchValue = matchValue + "%";
		} else if (matchType == MatchType.LIKER && propertyClass == String.class) {
			matchValue = "%" + matchValue;
		}
		if (matchType == MatchType.LET && propertyClass == Date.class) {
			// 若为小于日期的，自动加上时间
			Date temp = (Date) matchValue;
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(temp);
			String date = calendar.get(Calendar.YEAR) + "-" + (calendar.get(Calendar.MONTH) + 1) + "-" + calendar.get(Calendar.DATE) + " 23:59:59";
			SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			try {
				matchValue = sf.parse(date);
			} catch (ParseException e) {
				e.printStackTrace();
			}

		}
		// this.matchValue =
		// org.apache.commons.beanutils.ConvertUtils.convert(value,
		// propertyClass);
	}

	/**
	 * @author ice
	 * @param
	 * @return 返回本PropertyFilter的JPQL语句
	 */
	public String toJpql() {
		StringBuilder sb = new StringBuilder();
		if (this.hasMultiProperties()) {
			sb.append(" (");
		}
		for (int i = 0; i < propertyFullNames.length; i++) {
			String propertyFullName = propertyFullNames[i];
			if (matchType == MatchType.IN) {
				if (i == 0) {
					sb.append(propertyFullName.replace('!', '.')).append(matchType.toString()).append(matchValue);
				} else {
					sb.append(" or ").append(propertyFullName.replace('!', '.')).append(matchType.toString()).append(matchValue);
				}
			} else {
				if (i == 0) {
					sb.append(propertyFullName.replace('!', '.')).append(matchType.toString()).append(":").append(propertyFullName.replace('!', '_').replace('.', '_')).append(getOrdernos()[i]);
				} else {
					sb.append(" or ").append(propertyFullName.replace('!', '.')).append(matchType.toString()).append(":").append(propertyFullName.replace('!', '_').replace('.', '_')).append(getOrdernos()[i]);
				}
			}
		}
		if (this.hasMultiProperties()) {
			sb.append(") ");
		}
		return sb.toString();
	}

	/**
	 * @author ice
	 * @param hasand
	 *            :返回的JPQL语句前是否加上 add 关键字
	 * @return 返回本PropertyFilter的JPQL语句
	 */
	public String toJpql(boolean PrefixAnd) {
		String jpql = toJpql();
		if (!"".equals(jpql) && PrefixAnd) {
			jpql = " and " + jpql;
		}
		return jpql;
	}

	private void setPropertyNameOrderno(int idx, int orderno) {
		this.getOrdernos()[idx] = orderno;
	}

	private static void setSameNameOrderNo(List<PropertyFilter> propertyFilterList) {
		if (propertyFilterList == null || propertyFilterList.size() <= 0) {
			return;
		}
		for (int i = 0; i < propertyFilterList.size(); i++) {
			String[] propertyNames = propertyFilterList.get(i).propertyNames;
			for (int k = 0; k < propertyNames.length; k++) {
				String propertyName = propertyNames[k];
				int orderno = 0;
				for (int j = 0; j < i; j++) {
					String[] _propertyNames = propertyFilterList.get(j).propertyNames;
					for (String _propertyname : _propertyNames) {
						if (_propertyname.equalsIgnoreCase(propertyName)) {
							orderno++;
						}
					}
				}
				propertyFilterList.get(i).setPropertyNameOrderno(k, orderno);
			}
		}
	}

	/**
	 * @author ice
	 * @param
	 * @return 返回所有PropertyFilter的JPQL语句
	 */
	public static String toJpql(List<PropertyFilter> propertyFilterList) {
		setSameNameOrderNo(propertyFilterList);
		StringBuilder sb = new StringBuilder();
		if (propertyFilterList != null) {
			for (int i = 0; i < propertyFilterList.size(); i++) {
				PropertyFilter propertyFilter = propertyFilterList.get(i);
				if (i == 0) {
					sb.append(propertyFilter.toJpql());
				} else {
					sb.append(" and ").append(propertyFilter.toJpql());
				}
			}
		}
		return sb.toString();
	}

	/**
	 * @author ice
	 * @param hasand
	 *            :返回的JPQL语句前是否加上 add 关键字
	 * @return 返回本PropertyFilter的JPQL语句
	 */
	public static String toJpql(List<PropertyFilter> propertyFilterList, boolean PrefixAnd) {
		String jpql = toJpql(propertyFilterList);
		if (!"".equals(jpql) && PrefixAnd) {
			jpql = " and " + jpql;
		}
		return jpql;
	}

	/**
	 * 获取比较值的类型.
	 */
	public Class<?> getPropertyClass() {
		return propertyClass;
	}

	/**
	 * 获取比较方式.
	 */
	public MatchType getMatchType() {
		return matchType;
	}

	/**
	 * 获取比较值.
	 */
	public Object getMatchValue() {
		return matchValue;
	}

	/**
	 * 获取比较属性名称列表.
	 */
	public String[] getPropertyFullNames() {
		return propertyFullNames;
	}

	/**
	 * 获取唯一的比较属性名称.
	 */
	public String getPropertyFullName() {
		// Assert.isTrue(propertyFullNames.length == 1,
		// "There are not only one property in this filter.");
		return propertyFullNames[0];
	}

	/**
	 * 获取比较属性名称列表.
	 */
	public String[] getPropertyNames() {
		return propertyNames;
	}

	/**
	 * 获取唯一的比较属性名称.
	 */
	public String getPropertyName() {
		// Assert.isTrue(propertyNames.length == 1,
		// "There are not only one property in this filter.");
		return propertyNames[0];
	}

	/**
	 * 是否比较多个属性.
	 */
	public boolean hasMultiProperties() {
		return (propertyFullNames.length > 1);
	}

	public void setOrdernos(Integer[] ordernos) {
		this.ordernos = ordernos;
	}

	public Integer[] getOrdernos() {
		return ordernos;
	}

	/**
	 * 转换字符串到相应类型.
	 * 
	 * @param value
	 *            待转换的字符串.
	 * @param toType
	 *            转换目标类型.
	 * @throws Exception 
	 */
	@SuppressWarnings("deprecation")
	public static Object convertStringToObject(String value, Class<?> toType) throws Exception {
		try {
			if (toType.toString().equals("class java.util.Date")) {
				String dateString = value;
				if (dateString.contains("下午")) {
					String yearday = dateString.substring(0, 10);
					int pos = dateString.indexOf(":");
					int hour = (new Integer(dateString.substring(pos - 2, pos)) + 12);
					String hourtime = hour + dateString.substring(pos, dateString.length());
					dateString = yearday + hourtime;
				}
				if (dateString.contains("上午")) {
					String yearday = dateString.substring(0, 10);
					int pos = dateString.indexOf(":");
					int hour = (new Integer(dateString.substring(pos - 2, pos)));
					String hourtime = hour + dateString.substring(pos, dateString.length());
					dateString = yearday + hourtime;
				}
				value = dateString;
				if (value.contains("CST")) {
					value = value.replace("CST", "GMT+800");
				}
				if (value.contains("GMT 800")) {
					value = value.replace("GMT 800", "GMT+800");
				}
				if (value.contains("GMT+800")) {
					return new Date(value);
				} else {
					if (value.indexOf("/") == 2) {
						value = value.replace("/", "-");
						value = value.substring(6, 10) + "-" + value.substring(0, 5);
					}
					boolean issmartgwtclient = value.contains("T");
					// if (issmartgwtclient){
					value = value.replace('T', ' ');
					if (value != null) {
						// 处理传过来没有时间的日期参数
						if (value.indexOf(":") == -1) {
							value = value + " 00:00:00";
						}
						SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.SIMPLIFIED_CHINESE);

						// Start ---Add by liaojc at 2012-12-13 解决
						// 日期格式“2012/12/12 00:00:00”引起的错误 ，改为“2012-12-12
						// 00:00:00”格式
						// String s4=value.substring(4, 5);
						// String s7=value.substring(7, 8);
						if (value.substring(4, 5).equals("/") && value.substring(7, 8).equals("/")) {
							String s[] = value.split("/");
							value = s[0] + "-" + s[1] + "-" + s[2];
						}

						// End ---
						java.util.Date _datetime = format.parse(value);
						if (issmartgwtclient) {
							Calendar calendar = Calendar.getInstance();
							calendar.setTime(_datetime);
							calendar.add(Calendar.HOUR, 8);
							_datetime = calendar.getTime();
						}
						return new Date(_datetime.getTime());
					}
					// }
					// else{
					// return
					// org.apache.commons.beanutils.ConvertUtils.convert(value,
					// toType);
					// }
				}
			}
			return org.apache.commons.beanutils.ConvertUtils.convert(value, toType);
		} catch (Exception e) {
			throw e;
		}
	}

}
